#include "../FastLogger.h"

#include "../Event.h"
#include "../Path.h"

#include <boost/thread.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/bind.hpp>
#include <boost/filesystem.hpp>


#include <deque>
#include <vector>
#include <fstream>
#include <algorithm>
#include <stdio.h>
#include <stdlib.h>

#include <windows.h>

namespace AlgoTrading
{
	namespace Logging
	{
		namespace Impl
		{
			class MessagesQueue
			{
			public:
				static const char * UNKNOWN_MESSAGE_TYPE_MESSAGE;
				MessagesQueue(const std::wstring& logFileName)
				{
					stream.open(logFileName.c_str(), std::wofstream::out | std::wofstream::app);
				}

				virtual ~MessagesQueue()
				{
					Close();
				}

				void Close()
				{
					if (stream.is_open())
					{
						stream.close();
					}
				}

				void AddToQueue(void* object, FastLogger::ObjectType objectType)
				{
					boost::lock_guard<boost::mutex> lock(guard);
					messagesQueue.push_back(OneMessage(object, objectType, GetCurrentTime()));
				}

				void PrintAllMessages()
				{
					std::vector<OneMessage> messagesToFlush;
					{
						boost::lock_guard<boost::mutex> lock(guard);
						messagesToFlush.reserve(messagesQueue.size());
						while(!messagesQueue.empty())
						{
							messagesToFlush.push_back(messagesQueue.front());
							messagesQueue.pop_front();
						}
					}
					for(std::vector<OneMessage>::iterator it = messagesToFlush.begin(); it != messagesToFlush.end(); ++it)
					{
						SYSTEMTIME st;
						FileTimeToSystemTime(&it->Time, &st);

						wchar_t buffer[50];
						wsprintf(buffer, L"%02d:%02d:%02d.%03d: ", st.wHour, st.wMinute, st.wSecond, st.wMilliseconds);
			
						stream << buffer;

						switch(it->ObjectType)
						{
						case FastLogger::STRING:
							{
								std::auto_ptr<std::string> stringPtr((std::string*)it->Object);
								std::wstring wstr(stringPtr->length(), L' ');
								std::copy(stringPtr->begin(), stringPtr->end(), wstr.begin());
								stream << wstr;
								break;
							}
						case FastLogger::WIDE_STRING:
							{
								std::auto_ptr<std::wstring> wstringPtr((std::wstring*)it->Object);
								stream << *wstringPtr;
								break;
							}
						default:
							stream << UNKNOWN_MESSAGE_TYPE_MESSAGE;
						}
						stream << std::endl;
					}
					stream.flush();
				}


			private:
				_FILETIME GetCurrentTime()
				{
					DWORD ticks = ::GetTickCount();
					ULARGE_INTEGER ulNowTime;
					_FILETIME ft = beginTime;
					ulNowTime.HighPart = ft.dwHighDateTime;
					ulNowTime.LowPart = ft.dwLowDateTime;
					ulNowTime.QuadPart += ((ticks - beginTickCount) *10000);

					ft.dwHighDateTime = ulNowTime.HighPart;
					ft.dwLowDateTime = ulNowTime.LowPart;
					return ft;
				}

				class OneMessage
				{
				public:
					OneMessage(void* object, FastLogger::ObjectType objectType, _FILETIME time)
						: Object(object)
						, ObjectType(objectType)
						, Time(time)
					{
					}
					void* Object;
					FastLogger::ObjectType ObjectType;
					_FILETIME Time;
				};
				std::deque<OneMessage> messagesQueue;
				boost::mutex guard;
				std::wofstream stream;
				static _FILETIME beginTime;
				static DWORD beginTickCount;
			};

			_FILETIME GetInitialFileTime()
			{
				SYSTEMTIME sm;
				GetLocalTime(&sm);
				_FILETIME ft;
				SystemTimeToFileTime(&sm, &ft);
				return ft;
			}

			_FILETIME MessagesQueue::beginTime = GetInitialFileTime();
			DWORD MessagesQueue::beginTickCount = ::GetTickCount();
			const char * MessagesQueue::UNKNOWN_MESSAGE_TYPE_MESSAGE = "Unknown log object type";
		}

		std::wstring GetLogName()
		{
			std::wstring path(AlgoTrading::Common::GetCurrentExecutableDirctoryPath());

			wchar_t buffer[50];
			SYSTEMTIME sm;
			GetLocalTime(&sm);
			wsprintf(buffer, L"%04d%02d%02d_%02d_%02d_%02d.log", sm.wYear, sm.wMonth, sm.wDay, sm.wHour, sm.wMinute, sm.wSecond);

			return AlgoTrading::Common::CombinePath(path, std::wstring(buffer));
		}

		AlgoTrading::Logging::Impl::MessagesQueue FastLogger::queue(GetLogName());

		std::auto_ptr<AlgoTrading::Common::Event> FastLogger::stopEvent(AlgoTrading::Common::CreateAutoResetEvent(false));

		std::auto_ptr<boost::thread> FastLogger::thread(0);

		volatile bool FastLogger::started = false;


		FastLogger::FastLogger(void)
		{
		}

		FastLogger::~FastLogger(void)
		{
			Stop();
			queue.PrintAllMessages();
			queue.Close();
		}

		void FastLogger::Start()
		{
			started = true;
			thread.reset(new boost::thread(boost::bind(&FastLogger::ProcessingCycle, this)));
		}

		void FastLogger::Stop()
		{
			if (started)
			{
				started = false;
				stopEvent->Set();
				thread->join();
 				thread.reset();
			}
		}

		void FastLogger::Log(void* object, ObjectType objectType)
		{
			queue.AddToQueue(object, objectType);
		}

		void FastLogger::ProcessingCycle()
		{
			for(;;)
			{
				if (stopEvent->Wait(500))
				{
					queue.Close();
					return;
				}
				queue.PrintAllMessages();
			}
		}
	}
}

AlgoTrading::Logging::FastLogger AlgoTrading::Logging::fastLogger;




